package org.iabako.client.ui.form.business.sale;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import org.iabako.client.config.page.PageEnum;
import org.iabako.client.navigation.NavigationController;
import org.iabako.client.tools.IabakoAsyncCallback;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.form.AbstractForm;
import org.iabako.client.ui.layout.field.*;
import org.iabako.client.ui.layout.widget.datatable.CustomColumnDefinition;
import org.iabako.client.ui.layout.widget.datatable.CustomColumnEventHandler;
import org.iabako.client.ui.layout.widget.dialogbox.IabakoDialogBox;
import org.iabako.client.ui.layout.widget.payment.PaymentTable;
import org.iabako.client.ui.layout.widget.tooltip.TooltipListener;
import org.iabako.client.ui.layout.widget.validation.ValidationMessage;
import org.iabako.client.user.AuthenticateUserProvider;
import org.iabako.shared.entity.business.*;
import org.iabako.shared.entity.enumeration.Country;
import org.iabako.shared.entity.enumeration.InvoiceRecipientType;
import org.iabako.shared.entity.enumeration.LabelEnum;
import org.iabako.shared.entity.enumeration.TrackingType;
import org.iabako.shared.iservice.function.InvoiceGeneratorServiceAsync;
import org.iabako.shared.querydefinition.columndefinition.PaymentColumnEnum;
import org.iabako.shared.querydefinition.dto.QueryResultDTO;
import org.iabako.shared.querydefinition.dto.QueryResultValue;
import org.iabako.shared.tools.GenericTools;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by jose on 05/02/15.
 */
public class SaleInvoiceForm extends AbstractForm {

    interface MyUiBinder extends UiBinder<HTMLPanel, SaleInvoiceForm> {
    }

    protected MyUiBinder binderForm = GWT.create(MyUiBinder.class);

    protected final InvoiceGeneratorServiceAsync invoiceGeneratorServiceAsync = InvoiceGeneratorServiceAsync.Util.getInstance();

    @UiField
    FormPanel invoiceGeneratorForm;
    @UiField
    InputElement invoiceGeneratorFileToken;
    @UiField
    HTMLPanel invoicePanel;
    @UiField
    InputText addressLine1;
    @UiField
    InputText addressLine2;
    @UiField
    InputText city;
    @UiField
    SelectList country;
    @UiField
    InputText zipCode;
    @UiField
    InputText phoneNumber;
    @UiField
    RadioButtonList invoiceRecipientType;
    @UiField
    DivElement mailGlobalInvoice;
    @UiField
    DivElement downloadGlobalInvoice;
    @UiField
    HTMLPanel receiptsPanel;
    @UiField
    LabelText invoiceGlobalDate;
    @UiField
    LabelText invoiceGlobalTotal;
    @UiField
    LabelText invoiceGlobalNumber;
    @UiField
    InputText name;
    @UiField
    InputTextEmail email;

    private SalePaymentsForm salePaymentsForm;
    private CustomColumnDefinition generateInvoiceAction;
    private CustomColumnDefinition generateMailAction;
    private PaymentTable invoiceTable;

    Map<Long, String> invoiceNumberMap = new HashMap<Long, String>();

    public SaleInvoiceForm() {
        initWidget(binderForm.createAndBindUi(this));

        Element divIconMail = DOM.createDiv();
        divIconMail.setClassName("mailinvoiceicon");
        generateMailAction = new CustomColumnDefinition("columnSplus", "columnSplus tdinvoiceaction", "invoice_form_mail_action_tooltip", divIconMail, new SendInvoiceByMailHandler());

        Element divIconInvoice = DOM.createDiv();
        divIconInvoice.setClassName("downloadinvoiceicon");
        generateInvoiceAction = new CustomColumnDefinition("columnSplus", "columnSplus tdinvoiceaction", "invoice_form_download_action_tooltip", divIconInvoice, new GenerateInvoiceHandler());

        DOM.sinkEvents(mailGlobalInvoice.getParentElement(), Event.MOUSEEVENTS);
        DOM.setEventListener(mailGlobalInvoice.getParentElement(), new TooltipListener(Messages.getLabel("invoice_form_mail_action_tooltip")));
        DOM.sinkEvents(downloadGlobalInvoice.getParentElement(), Event.MOUSEEVENTS);
        DOM.setEventListener(downloadGlobalInvoice.getParentElement(), new TooltipListener(Messages.getLabel("invoice_form_download_action_tooltip")));
    }

    public void setInfo(Sale sale, SalePaymentsForm salePaymentsForm) {

        if (entity == null) {
            entity = sale;

            invoiceRecipientType.setRawValue(sale.getInvoiceRecipient() != null ? InvoiceRecipientType.other : InvoiceRecipientType.client);
            checkClientComplete(false);
            setInvoiceRecipientData();

            if (sale.getClient() == null) {
                invoiceRecipientType.setVisible(false);
            } else {
                setInvoiceRecipientHandler();
            }
        }

        invoiceGlobalNumber.setLabel(sale.getNumber());
        invoiceGlobalDate.setLabel(GenericTools.formatDate(sale.getDate()));

        //If we can't get it from label, maybe it's first loading (async call not yet finished), then we can get it from Sale object
        String totalAmount = salePaymentsForm.paymentResume.getTotalAmountLabel().getLabelText() != null ?
                salePaymentsForm.paymentResume.getTotalAmountLabel().getLabelText()
                : GenericTools.formatAmount(sale.getRecalculatedTotal() + "");

        invoiceGlobalTotal.setLabel(totalAmount);

        if (invoiceTable == null) {
            this.salePaymentsForm = salePaymentsForm;
            createInvoiceTable();
        }

        invoiceTable.queryDefinitionDTO = salePaymentsForm.getPaymentTable().queryDefinitionDTO;
        invoicePanel.clear();
        invoiceTable.render();

        calculateGlobalInvoiceVisibility(this.salePaymentsForm);
    }

    public synchronized void calculateGlobalInvoiceVisibility(SalePaymentsForm salePaymentsForm) {
        PaymentTable installmentsTable = salePaymentsForm.getInstallmentTable();
        int installmentsNumber = installmentsTable != null ? installmentsTable.queryDefinitionDTO.getCountTotal() : 0;
        int invoicesNumber = invoiceTable != null ? invoiceTable.queryDefinitionDTO.getCountTotal() : 0;

        if (invoicesNumber == 1 && installmentsNumber == 0) {
            receiptsPanel.setVisible(false);
        } else {
            receiptsPanel.setVisible(true);
        }
    }

    private void setInvoiceRecipientHandler() {
        for (LabelEnum radioKey : invoiceRecipientType.getRadioButtons().keySet()) {
            if (InvoiceRecipientType.client.equals(radioKey)) {
                invoiceRecipientType.getRadioButtons().get(radioKey).addHandler(new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        checkClientComplete(true);
                        setInvoiceRecipientData();
                    }
                }, ClickEvent.getType());
            } else {
                invoiceRecipientType.getRadioButtons().get(radioKey).addHandler(new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        setInvoiceRecipientData();
                    }
                }, ClickEvent.getType());
            }
        }
    }

    private void checkClientComplete(boolean showAlert) {

        Client client = ((Sale) entity).getClient();
        boolean hasClient = client != null;
        boolean clientIncomplete = !hasClient || client.getEmail() == null || client.getAddress() == null
                || client.getAddress().getAddressLine1() == null || client.getAddress().getCity() == null
                || client.getAddress().getCountry() == null || client.getPhoneNumber1() == null;

        if (this.invoiceRecipientType.getValue() == InvoiceRecipientType.client && clientIncomplete) {

            this.invoiceRecipientType.setValue(InvoiceRecipientType.other);

            if (showAlert) {
                IabakoDialogBox iabakoDialogBox = new IabakoDialogBox("invoice_receipt_client_incomplete_alert_title",
                        "invoice_receipt_client_incomplete_alert_text", true,
                        "invoice_receipt_client_incomplete_alert_go_to_client") {
                    @Override
                    protected void customConfirmButtonHandler() {
                        NavigationController.getInstance().goTo(PageEnum.ClientModify, parentForm, ((Sale) entity).getClient().getId() + "");
                    }
                };
                iabakoDialogBox.setAvoidClosePopupCall(true);
                iabakoDialogBox.show();
            }
        }
    }

    private void setInvoiceRecipientData() {

        Client client = ((Sale) entity).getClient();
        boolean hasClient = client != null;

        boolean hasRecipient = invoiceRecipientType.getValue() == InvoiceRecipientType.other || !hasClient;

        InvoiceRecipient invoiceRecipient = ((Sale) entity).getInvoiceRecipient();
        if (invoiceRecipient == null) {
            invoiceRecipient = new InvoiceRecipient();
        }
        String name = hasRecipient ? invoiceRecipient.getName() : client.getName();
        String email = hasRecipient ? invoiceRecipient.getEmail() : client.getEmail();
        String address1 = hasRecipient ? invoiceRecipient.getAddress().getAddressLine1() : client.getAddress().getAddressLine1();
        String address2 = hasRecipient ? invoiceRecipient.getAddress().getAddressLine2() : client.getAddress().getAddressLine2();
        String city = hasRecipient ? invoiceRecipient.getAddress().getCity() : client.getAddress().getCity();
        Country country = hasRecipient ? invoiceRecipient.getAddress().getCountry() : client.getAddress().getCountry();
        String zipCode = hasRecipient ? invoiceRecipient.getAddress().getZipCode() : client.getAddress().getZipCode();
        String phoneNumber1 = hasRecipient ? invoiceRecipient.getPhoneNumber() : client.getPhoneNumber1();

        this.name.clearErrorMessage(this.name, "");
        this.email.clearErrorMessage(this.email, "");
        this.addressLine1.clearErrorMessage(this.addressLine1, "");
        this.city.clearErrorMessage(this.city, "");
        this.country.clearErrorMessage(this.country, "");
        this.phoneNumber.clearErrorMessage(this.phoneNumber, "");

        this.name.setEnabled(hasRecipient);
        this.email.setEnabled(hasRecipient);
        this.addressLine1.setEnabled(hasRecipient);
        this.addressLine2.setEnabled(hasRecipient);
        this.city.setEnabled(hasRecipient);
        this.country.setEnabled(hasRecipient);
        this.zipCode.setEnabled(hasRecipient);
        this.phoneNumber.setEnabled(hasRecipient);

        this.name.setValue(name);
        this.email.setValue(email);
        this.addressLine1.setValue(address1);
        this.addressLine2.setValue(address2);
        this.city.setValue(city);
        this.country.setEnumValue(country);
        this.zipCode.setValue(zipCode);
        this.phoneNumber.setValue(phoneNumber1);
    }

    public class SendInvoiceByMailHandler extends CustomColumnEventHandler {

        @Override
        public CustomColumnEventHandler getClone() {
            SendInvoiceByMailHandler transferHandler = new SendInvoiceByMailHandler();
            transferHandler.setDivElement(divElement);
            return transferHandler;
        }

        public void onBrowserEvent(Event event) {
            event.stopPropagation();
            Long idPayment = Long.parseLong(divElement.getId().replace("custom_action_", ""));
            generateInvoiceActionHandler(idPayment, true);
        }
    }

    private void generateInvoiceActionHandler(Long idPayment, boolean sendByEmail) {
        if (!validate()) {
            return;
        }

        generateInvoice(idPayment, invoiceNumberMap.get(idPayment), sendByEmail);
    }

    public class GenerateInvoiceHandler extends CustomColumnEventHandler {

        @Override
        public CustomColumnEventHandler getClone() {
            GenerateInvoiceHandler transferHandler = new GenerateInvoiceHandler();
            transferHandler.setDivElement(divElement);
            return transferHandler;
        }

        public void onBrowserEvent(Event event) {
            event.stopPropagation();
            Long idPayment = Long.parseLong(divElement.getId().replace("custom_action_", ""));
            generateInvoiceActionHandler(idPayment, false);
        }
    }

    private void createInvoiceTable() {
        invoiceTable = new PaymentTable(null, null, null, null,
                Arrays.asList(generateMailAction, generateInvoiceAction), false, false) {
            @Override
            protected void customTaskBeforeRender() {
                goToModifyPage = null;

                queryDefinitionDTO.getColumnsExclusion().remove(PaymentColumnEnum.invoiceNumber);
                queryDefinitionDTO.getColumnsExclusion().add(PaymentColumnEnum.scheduleDate);
                queryDefinitionDTO.getColumnsExclusion().add(PaymentColumnEnum.comment);

                int index = 1;
                for (QueryResultDTO result : queryDefinitionDTO.getResults().values()) {
                    QueryResultValue qrValue = new QueryResultValue();
                    qrValue.setValue(((Sale) entity).getNumber() + "-" + index++);
                    result.getValues().put(PaymentColumnEnum.invoiceNumber, qrValue);
                    invoiceNumberMap.put(result.getId(), (String) qrValue.getValue());
                }
            }
        };
        invoiceTable.deleteActionEnabled = false;

        invoiceTable.resultsPanel = invoicePanel;

        DOM.sinkEvents(downloadGlobalInvoice, Event.ONCLICK);
        DOM.setEventListener(downloadGlobalInvoice, new EventListener() {
            public void onBrowserEvent(Event event) {
                if (!validate()) {
                    return;
                }
                entity = ((SaleModifyForm) parentForm).getEntity();
                generateInvoice(null, null, false);
            }
        });

        DOM.sinkEvents(mailGlobalInvoice, Event.ONCLICK);
        DOM.setEventListener(mailGlobalInvoice, new EventListener() {
            public void onBrowserEvent(Event event) {
                if (!validate()) {
                    return;
                }
                entity = ((SaleModifyForm) parentForm).getEntity();
                generateInvoice(null, null, true);
            }
        });
    }

    private void generateInvoice(Long idPayment, String invoiceNumber, boolean sendByEmail) {
        //Refresh Sale from saleRecapForm (useful because changes are possible)
        entity = ((SaleModifyForm) parentForm).getEntity();

        if (sendByEmail) {
            SendInvoiceByEmail sendInvoiceByEmail = new SendInvoiceByEmail((Sale) entity, idPayment, invoiceNumber, false);
            NavigationController.getInstance().goTo(sendInvoiceByEmail);
        } else {

            int numberOfPayments = salePaymentsForm.getPaymentTable().queryDefinitionDTO.getCountTotal();
            int numberOfInstallments = salePaymentsForm.getInstallmentTable().queryDefinitionDTO.getCountTotal();

            boolean oneShotSale = numberOfPayments == 1 && numberOfInstallments == 0;

            invoiceGeneratorServiceAsync.generateInvoice((Sale) entity, oneShotSale ? null : idPayment, invoiceNumber, new IabakoAsyncCallback<String>() {
                public void onSuccess(String result) {
                    invoiceGeneratorForm.setMethod("GET");
                    invoiceGeneratorFileToken.setValue(result);
                    invoiceGeneratorForm.submit();

                    AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.invoiceDownloaded);

                    super.onSuccess(result);
                }
            });
        }
    }

    @Override
    protected List<IField> getMandatoryFields() {
        return null;
    }

    protected InvoiceRecipient getInvoiceRecipient() {
        if (invoiceRecipientType.getValue() == InvoiceRecipientType.other || ((Sale) entity).getClient() == null) {

            InvoiceRecipient invoiceRecipient = ((Sale) entity).getInvoiceRecipient();

            //Silent validation
            if (!validate(true)) {
                return invoiceRecipient;
            }

            if (invoiceRecipient == null) {
                invoiceRecipient = new InvoiceRecipient();
            }
            invoiceRecipient.setName(name.getValue());
            invoiceRecipient.setEmail(email.getValue());
            //for automatic calculation
            invoiceRecipient.getName();

            Address address = new Address();
            address.setAddressLine1(addressLine1.getValue());
            address.setAddressLine2(addressLine2.getValue());
            address.setCity(city.getValue());
            address.setCountry((Country) country.getEnumValue());
            address.setZipCode(zipCode.getValue());
            invoiceRecipient.setAddress(address);

            invoiceRecipient.setPhoneNumber(phoneNumber.getValue());

            return invoiceRecipient;

        } else {
            return null;
        }
    }

    @Override
    protected AbstractEntity getEntity() {
        return null;
    }

    @Override
    public boolean validate() {
        return validate(false);
    }

    private boolean validate(boolean silentValidation) {
        Sale sale = (Sale) entity;

        if (invoiceRecipientType.getValue() == InvoiceRecipientType.client
                && sale.getClient() != null) {
            return true;
        }

        boolean atLeastOneError = false;

        if (GenericTools.isEmpty(name.getValue())) {
            name.addErrorMessage(name, true, "");
            atLeastOneError = true;
        }
        if (GenericTools.isEmpty(email.getValue()) || !email.isValid()) {
            email.addErrorMessage(email, true, "");
            atLeastOneError = true;
        }
        if (GenericTools.isEmpty(addressLine1.getValue())) {
            addressLine1.addErrorMessage(addressLine1, true, "");
            atLeastOneError = true;
        }
        if (GenericTools.isEmpty(city.getValue())) {
            city.addErrorMessage(city, true, "");
            atLeastOneError = true;
        }
        if (GenericTools.isEmpty(country.getEnumValue())) {
            country.addErrorMessage(country, true, "");
            atLeastOneError = true;
        }
        if (GenericTools.isEmpty(phoneNumber.getValue())) {
            country.addErrorMessage(phoneNumber, true, "");
            atLeastOneError = true;
        }

        if (!silentValidation && atLeastOneError) {
            ValidationMessage.getFirstFromDOM().showErrorMessage(true, true, "invoice_form_no_mandatory_info_error");
        }

        return !atLeastOneError;
    }
}
